package com.immediatus.ui;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout.LayoutParams;
import com.immediatus.engine.Engine;
import com.immediatus.engine.contracts.IAccelerometerListener;
import com.immediatus.engine.contracts.ILocationListener;
import com.immediatus.engine.contracts.IOrientationListener;
import com.immediatus.engine.layer.Canvas;
import com.immediatus.graphics.managers.FontManager;
import com.immediatus.graphics.managers.TextureManager;
import com.immediatus.options.*;
import com.immediatus.ui.contracts.IProgressListener;
import com.immediatus.ui.util.Callback;
import com.immediatus.ui.util.ProgressCallable;
import com.immediatus.ui.view.RenderView;

import java.util.concurrent.Callable;


public abstract class BaseActivity extends Activity implements IInteraction{

    private Engine _engine;
    private RenderView _renderView;
    private boolean _isWindowFocused;

    private PowerManager.WakeLock _wakeLock;
    private boolean _paused;
    private boolean gameLoaded;

    public Engine getEngine(){
        return this._engine;
    }

    public TextureManager getTextureManager(){
        return this._engine.getTextureManager();
    }

    public FontManager getFontManager(){
        return this._engine.getFontManager();
    }

    @Override
    protected void onCreate(final Bundle instanceState_){
        super.onCreate(instanceState_);
        this._paused = true;
        this._engine = this.onLoadEngine();
        this.applyEngineOptions(this._engine.getEngineOptions());

        this.onSetContentView();
    }

    @Override
    protected void onResume(){
        super.onResume();

        if (this._paused && this._isWindowFocused){
            this.doResume();
        }
    }

    @Override
    public void onWindowFocusChanged(final boolean hasWindowFocus_){
        super.onWindowFocusChanged(hasWindowFocus_);

        if (hasWindowFocus_){
            if (this._paused){
                this.doResume();
            }
            this._isWindowFocused = true;
        } else{
            if (!this._paused){
                this.doPause();
            }
            this._isWindowFocused = false;
        }
    }

    @Override
    protected void onPause(){
        super.onPause();

        if (!this._paused){
            this.doPause();
        }
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        this._engine.interruptUpdateThread();
        this.onUnloadResources();
    }

    @Override
    public void onUnloadResources(){
    }

    @Override
    public void onInteractionResume(){

    }

    @Override
    public void onInteractionPause(){

    }

    public void runOnUpdateThread(final Runnable runnable_){
        this._engine.runOnUpdateThread(runnable_);
    }

    protected void onSetContentView(){
        this._renderView = new RenderView(this);
        this._renderView.setEGLConfigChooser(8, 8, 8, 8, 8, 0);
        this._renderView.getHolder().setFormat(PixelFormat.RGBA_8888);
        this._renderView.setRenderer(this._engine);
        this.setContentView(this._renderView, this.createSurfaceViewLayoutParams());
    }

    private void acquireWakeLock(final WakeLockOptions wakeLockOptions_){
        if (wakeLockOptions_ == WakeLockOptions.SCREEN_ON){
            keepScreenOn();
        } else{
            final PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
            this._wakeLock = pm.newWakeLock(wakeLockOptions_.getFlag() | PowerManager.ON_AFTER_RELEASE, "AndEngine");
            try{
                this._wakeLock.acquire();
            } catch (final SecurityException e){
            }
        }
    }

    private void releaseWakeLock(){
        if (this._wakeLock != null && this._wakeLock.isHeld()){
            this._wakeLock.release();
        }
    }

    private void applyEngineOptions(final EngineOptions engineOptions_){
        if (engineOptions_.isFullscreen()){
            requestFullscreen();
        }

        switch (engineOptions_.getScreenOrientation()){
            case LANDSCAPE:
                this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                break;
            case PORTRAIT:
                this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                break;
        }
    }

    protected LayoutParams createSurfaceViewLayoutParams(){
        final LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        layoutParams.gravity = Gravity.CENTER;
        return layoutParams;
    }

    protected void enableVibrator(){
        this._engine.enableVibrator(this);
    }

    protected void enableLocationSensor(final ILocationListener listener_, final LocationSensorOptions options_){
        this._engine.enableLocationSensor(this, listener_, options_);
    }

    protected void disableLocationSensor(){
        this._engine.disableLocationSensor(this);
    }

    protected boolean enableAccelerometerSensor(final IAccelerometerListener listener_){
        return this._engine.enableAccelerometerSensor(this, listener_);
    }

    protected boolean enableAccelerometerSensor(final IAccelerometerListener listener_, final AccelerometerSensorOptions options_){
        return this._engine.enableAccelerometerSensor(this, listener_, options_);
    }

    protected boolean disableAccelerometerSensor(){
        return this._engine.disableAccelerometerSensor(this);
    }

    protected boolean enableOrientationSensor(final IOrientationListener pOrientationListener){
        return this._engine.enableOrientationSensor(this, pOrientationListener);
    }

    protected boolean enableOrientationSensor(final IOrientationListener listener_, final OrientationSensorOptions options_){
        return this._engine.enableOrientationSensor(this, listener_, options_);
    }

    protected boolean disableOrientationSensor(){
        return this._engine.disableOrientationSensor(this);
    }

    protected <T> void doAsync(final int titleResID_, final int messageResID_, final Callable<T> callable_, final Callback<T> callback_){
        doAsync(this, this.getString(titleResID_), this.getString(messageResID_), callable_, callback_, null, false);
    }

    protected <T> void doAsync(final int titleResID_, final int messageResID_, final Callable<T> callable_, final Callback<T> callback_, final Callback<Exception> exceptionCallback_){
        doAsync(this, this.getString(titleResID_), this.getString(messageResID_), callable_, callback_, exceptionCallback_, false);
    }

    protected <T> void doProgressAsync(final int titleResID_, final ProgressCallable<T> callable_, final Callback<T> callback_){
        doProgressAsync(this, titleResID_, callable_, callback_, null);
    }

    protected <T> void doProgressAsync(final int titleResID_, final ProgressCallable<T> callable_, final Callback<T> callback_, final Callback<Exception> exceptionCallback_){
        doProgressAsync(this, titleResID_, callable_, callback_, exceptionCallback_);
    }

    private void doResume(){
        if (!this.gameLoaded){
            this.onLoadResources();
            final Canvas canvas = this.onLoadLayer();
            this._engine.onLoadComplete(canvas);
            this.onLoadComplete();
            this.gameLoaded = true;
        }

        this._paused = false;
        this.acquireWakeLock(this._engine.getEngineOptions().getWakeLockOptions());
        this._engine.onResume();

        this._renderView.onResume();
        this._engine.start();
        this.onInteractionResume();
    }

    private void doPause(){
        this._paused = true;
        this.releaseWakeLock();

        this._engine.onPause();
        this._engine.stop();
        this._renderView.onPause();
        this.onInteractionPause();
    }

    private void keepScreenOn(){
        this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    private void requestFullscreen(){
        final Window window = this.getWindow();
        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        window.requestFeature(Window.FEATURE_NO_TITLE);
    }

    private <T> void doAsync(final Context context_, final CharSequence title_, final CharSequence message_, final Callable<T> callable_, final Callback<T> callback_, final Callback<Exception> exceptionCallback_, final boolean cancelable_){
        new AsyncTask<Void, Void, T>(){
            private ProgressDialog mPD;
            private Exception mException = null;

            @Override
            public void onPreExecute(){
                this.mPD = ProgressDialog.show(context_, title_, message_, true, cancelable_);
                if (cancelable_){
                    this.mPD.setOnCancelListener(new DialogInterface.OnCancelListener(){
                        @Override
                        public void onCancel(final DialogInterface pDialogInterface){
                            exceptionCallback_.onCallback(new CancelledException());
                            pDialogInterface.dismiss();
                        }
                    });
                }
                super.onPreExecute();
            }

            @Override
            public T doInBackground(final Void... params){
                try{
                    return callable_.call();
                } catch (final Exception e){
                    this.mException = e;
                }
                return null;
            }

            @Override
            public void onPostExecute(final T result){
                try{
                    this.mPD.dismiss();
                } catch (final Exception e){
                }

                if (this.isCancelled()){
                    this.mException = new CancelledException();
                }

                if (this.mException == null){
                    callback_.onCallback(result);
                } else{
                    if (exceptionCallback_ != null){
                        exceptionCallback_.onCallback(this.mException);
                    }
                }

                super.onPostExecute(result);
            }
        }.execute((Void[]) null);
    }

    private <T> void doProgressAsync(final Context context_, final int titleResID_, final ProgressCallable<T> callable_, final Callback<T> callback_, final Callback<Exception> exceptionCallback_){
        new AsyncTask<Void, Integer, T>(){
            private ProgressDialog mPD;
            private Exception mException = null;

            @Override
            public void onPreExecute(){
                this.mPD = new ProgressDialog(context_);
                this.mPD.setTitle(titleResID_);
                this.mPD.setIcon(android.R.drawable.ic_menu_save);
                this.mPD.setIndeterminate(false);
                this.mPD.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                this.mPD.show();
                super.onPreExecute();
            }

            @Override
            public T doInBackground(final Void... params_){
                try{
                    return callable_.call(new IProgressListener(){
                        @Override
                        public void onProgressChanged(final int progress_){
                            onProgressUpdate(progress_);
                        }
                    });
                } catch (final Exception e){
                    this.mException = e;
                }
                return null;
            }

            @Override
            public void onProgressUpdate(final Integer... values_){
                this.mPD.setProgress(values_[0]);
            }

            @Override
            public void onPostExecute(final T result_){
                try{
                    this.mPD.dismiss();
                } catch (final Exception e){
                }

                if (this.isCancelled()){
                    this.mException = new CancelledException();
                }

                if (this.mException == null){
                    callback_.onCallback(result_);
                } else{
                    if (exceptionCallback_ != null){
                        exceptionCallback_.onCallback(this.mException);
                    }
                }

                super.onPostExecute(result_);
            }
        }.execute((Void[]) null);
    }

    public static class CancelledException extends Exception{}
}
